import os, re,warnings
from functools import wraps
from typing import Literal, List, Any, Dict
from cy_query.utils.date_time import DateTime

T = Literal["str", "int", "float", 'DateTime', 'date', 'time', 'datetime', 'bool', 'list']
Extra = Literal['forbid', 'ignore', 'allow'] # 报错,忽略,保留
BOOL_MAP = {'true': True, 'false': False, '1': True, '0': False, 't': True, 'f': False, 'True': True, 'False': False,
            'TRUE': True, 'FALSE': False, '是': True, '否': False, 'Y': True, 'N': False, 'y': True, 'n': False,
            'yes': True, 'no': False, 'Yes': True, 'No': False, 'YES': True, 'NO': False, True: True, False: False,
            1: True, 0: False}

class CheckFieldError(Exception):
    pass


class CustomCheck:
    _default = object()
    
    def __init__(self, required: bool = True, msg: str = '', dtype: T = None, value_exist: bool = True,
                 regex: str = None, choices: List = None, default: Any = _default, children=None,help_text:str='',to_field=None):
        if isinstance(default,(list,dict)):
            warnings.warn("list or dict The instance may have an impact",UserWarning)
        self.required = required
        self.msg = msg
        self.dtype = dtype
        self.value_exist = value_exist
        self.regex = regex
        self.choices = choices
        if default == list:
            default = list()
        elif default == dict:
            default = dict()
        self.default = default
        self.name = None
        self.children = children
        self.help_text = help_text
        self._to_field = to_field
        if isinstance(self.children, type):
            self.children = self.children()
        if self.dtype == 'list':
            if self.children is None:
                self.children = CustomCheck()
            if not isinstance(self.children, (CustomCheck, BaseModel)):
                raise CheckFieldError("children is instance CustomCheck")
    
    @property
    def to_field(self):
        return self._to_field or self.name

    def __call__(self, value):
        if self.default is not self._default and value is None:
            if self.dtype == int and value is None:
                value = self.default
            else:
                value = self.default
        if self.required and value is None:
            raise CheckFieldError("%s 是必传参数" % (self.msg or self.name))
        if self.value_exist:
            if not value and not (self.dtype == int and value == 0) and self.required:
                raise CheckFieldError("%s 传入的数据为空" % (self.msg or self.name))
        if value is None:
            return self._default
        try:
            value = self.type_conversion(value)
        except:
            raise CheckFieldError("%s 传入的数据类型错误,期望类型是:%s,但传入的是%s" % (
                self.msg or self.name, self.dtype, type(value).__name__))
        if self.children:
            self.children.name = self.name
            if isinstance(self.children, CustomCheck):
                value = [self.children(i) for i in value]
            else:
                value = [self.children.handle_params(i) for i in value]
        if self.choices is not None:
            if value not in self.choices:
                raise CheckFieldError("%s 传入的数据不在可选范围内%s" % (self.msg or self.name, self.choices))
        if self.regex:
            if not bool(re.compile(self.regex).match(value)):
                raise CheckFieldError("%s 传入的数据不符合规则" % (self.msg or self.name))
        return value

    def type_conversion(self, value):
        if self.dtype == 'int':
            return int(value)
        if self.dtype == 'float':
            return float(value)
        if self.dtype == 'list':
            return value.split(',') if isinstance(value, str) else list(value)
        if self.dtype == 'dict':
            return value if isinstance(value, dict) else dict(value)
        if self.dtype == 'DateTime':
            return DateTime(value)
        if self.dtype == 'datetime':
            return DateTime(value).to_pydatetime()
        if self.dtype == 'time':
            return DateTime(value).time()
        if self.dtype == 'date':
            return DateTime(value).date()
        if self.dtype == 'bool':
            return BOOL_MAP.get(value, False)
        return str(value)

    def __repr__(self):
        return '<%s name=%s, required=%s, msg="%s">' % (self.__class__.__name__, self.name, self.required, self.msg)


class BaseModel:
    def __init__(self, extra: Extra = 'allow',attr:str=None,to_field=None, _kwargs: Dict = None):
        self.extra = extra
        self.attr = attr
        self.to_field = to_field
        self._data = _kwargs or {}

    def __call__(self, function):
        @wraps(function)
        def wrapper_function(*args, **kwargs):
            self._data = self.handle_params(kwargs)
            result = function(*args, BaseModel(_kwargs=self._data))
            return result
        return wrapper_function

    def check(self,kwargs):
        self._data = self.handle_params(kwargs)
        return self.__class__(_kwargs=self._data)

    def handle_params(self, data):
        from cy_query import Field
        if self.attr is not None:
            data = getattr(data,self.attr)
        dic = {}
        for i in dir(self):
            if i.startswith('_'): continue
            fun = getattr(self, i)
            if isinstance(fun,Field):
                fun = fun._to_custom_check()
            if isinstance(fun, CustomCheck):
                fun.name = i
                result = fun(data.get(i))
                if result is not CustomCheck._default:
                    dic[fun.to_field] = result
                    if fun._to_field:
                        data.pop(fun.name, None)
            elif isinstance(fun, BaseModel):
                if params := data.get(i):
                    dic[i] = fun.handle_params(params)
                else:
                    dic[fun.to_field or i] = fun.handle_params({})
        if self.extra == 'forbid' and dic.keys() != data.keys():
            raise CheckFieldError("未知参数:%s" % ','.join(set(data.keys()) - set(dic.keys())))
        elif self.extra == 'ignore':
            return dic
        data.update(dic)
        return data

    def to_dict(self):
        return self._data

    def __getattr__(self, item):
        return self._data[item]

    def __repr__(self):
        text = ['%s=%s' % (k, v) for k, v in self._data.items()]
        return '<%s %s>' % (self.__class__.__name__, ', '.join(text))
